package middle

import (
	"fmt"
	"ginweb-admin/commons/jwtgo"
	"ginweb-admin/commons/response"
	"ginweb-admin/global"
	"ginweb-admin/model/entity/jwt"
	"ginweb-admin/utils"
	"github.com/gin-gonic/gin"
	"strconv"
	"time"
)

var jwtService = jwtgo.JwtService{}

// 拦截中间件
func JWTAuth() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 通过http header中的token解析
		// 获取token
		token := c.GetHeader("Authorization")
		// 判断是否携带token
		if token == "" {
			response.Fail(4001, "请求未携带token，无访问权限！", c)
			c.Abort()
			return
		}

		// 获取uuid
		KvaUUID := c.GetHeader("KvaUUID")
		// 判断是否携带uuid
		if KvaUUID == "" {
			response.Fail(4001, "请求未携带token，无访问权限！", c)
			c.Abort()
			return
		}

		// 判断token是否在黑名单中
		blacklist := jwtService.IsBlacklist(token)
		if blacklist {
			response.Fail(4001, "token已进入黑名单", c)
			c.Abort()
			return
		}

		// 创建jwt对象
		myJwt := jwtgo.NewJWT()
		// 解析token
		customClaims, err := myJwt.ParserToken(token)
		// 解析失败，返回异常
		if err != nil {
			response.Fail(4001, "token失效了", c)
			c.Abort()
			return
		}

		// 从缓存中获取服务端用户uuid是否和用户传递进来的uuid是否一致，
		// 1:如果一致让操作，
		// 2:如果不一致直接提示你当前账号已经被挤下线了
		// 获取缓存中的uuid
		cacheUuid, _ := global.Cache.Get(fmt.Sprintf("LocalCache:Login:%d", customClaims.UserId))
		// 可能缓存被清理了
		if cacheUuid == "" {
			response.Fail(4001, "请求未携带登录标识，无权限访问", c)
			c.Abort()
			return
		}

		// 如果不相等，说明用户在别的地方登录了
		if cacheUuid != KvaUUID {
			response.Fail(4001, "账号已被挤下线！", c)
			c.Abort()
			return
		}

		// 判断是否需要续期
		fmt.Println("过期时间:", customClaims.ExpiresAt)
		fmt.Println("当前时间:", time.Now().Unix())
		fmt.Println("剩余时间：", customClaims.ExpiresAt-time.Now().Unix())
		fmt.Println("缓冲时间:", customClaims.BufferTime)
		// 判断过期时间 - 当前时间 < 缓冲时间  就开始续期
		if customClaims.ExpiresAt-time.Now().Unix() < customClaims.BufferTime {
			fmt.Println("开始续期时间...")
			// 设置7天过期时间
			epTime, _ := utils.ParseDuration("7d")
			// 生成新的过期时间：当前时间 + epTime
			customClaims.ExpiresAt = time.Now().Add(epTime).Unix()
			// 生成新的token
			newToken, _ := myJwt.CreateTokenByOldToken(token, *customClaims)
			// 输出给浏览器----request--header----给服务端
			// 输出给浏览器----response--header---给浏览器
			c.Header("new-authorization", newToken)
			c.Header("new-expires-at", strconv.FormatInt(customClaims.ExpiresAt, 10))
			// 如果生成新token了，旧的token怎么办？ jwt没有提供一个机制让旧token失效。
			_ = jwtService.JsonInBlacklist(jwt.JwtBlacklist{Jwt: token})
		}

		// 让后续的路由方法可以直接通过c.Get("claims")
		c.Set("claims", customClaims)
		c.Set("userId", customClaims.UserId)
		c.Next()
	}
}
